/**
 * js-booster - Utility functions
 * @version 1.1.1
 * @license MIT
 */

/**
 * Deep clone an object
 * @param {Object} obj Object to clone
 * @returns {Object} Deep cloned object
 */
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  
  const clone = Array.isArray(obj) ? [] : {};
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      clone[key] = deepClone(obj[key]);
    }
  }
  return clone;
}

/**
 * Clone an array
 * @param {Array} arr Array to clone
 * @returns {Array} Cloned array
 */
function cloneArray(arr) {
  return arr.slice();
}

/**
 * Debounce function
 * @param {Function} func Function to debounce
 * @param {number} delay Delay in milliseconds
 * @returns {Function} Debounced function
 */
function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

/**
 * Throttle function
 * @param {Function} func Function to throttle
 * @param {number} limit Time limit in milliseconds
 * @returns {Function} Throttled function
 */
function throttle(func, limit) {
  let lastFunc;
  let lastRan;
  return function(...args) {
    if (!lastRan) {
      func.apply(this, args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(() => {
        if (Date.now() - lastRan >= limit) {
          func.apply(this, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan));
    }
  };
}

/**
 * Deep equality check
 * @param {*} a First value
 * @param {*} b Second value
 * @returns {boolean} True if deeply equal
 */
function isEqual(a, b) {
  if (a === b) return true;
  if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
  
  const keysA = Object.keys(a);
  const keysB = Object.keys(b);
  
  if (keysA.length !== keysB.length) return false;
  
  for (const key of keysA) {
    if (!keysB.includes(key) || !isEqual(a[key], b[key])) return false;
  }
  
  return true;
}

/**
 * Generate random number in range
 * @param {number} min Minimum value
 * @param {number} max Maximum value
 * @returns {number} Random number
 */
function random(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Format timestamp to readable string
 * @param {Date|number|string} time Input time
 * @param {string} format Format string (default: 'YYYY-MM-DD HH:mm:ss')
 * @returns {string} Formatted time string
 */
function formatTime(time, format = 'YYYY-MM-DD HH:mm:ss') {
  const date = new Date(time);
  const map = {
    'YYYY': date.getFullYear(),
    'MM': String(date.getMonth() + 1).padStart(2, '0'),
    'DD': String(date.getDate()).padStart(2, '0'),
    'HH': String(date.getHours()).padStart(2, '0'),
    'mm': String(date.getMinutes()).padStart(2, '0'),
    'ss': String(date.getSeconds()).padStart(2, '0'),
  };
  
  return format.replace(/YYYY|MM|DD|HH|mm|ss/g, matched => map[matched]);
}

/**
 * Check if string is a valid URL
 * @param {string} str String to check
 * @returns {boolean} True if valid URL
 */
function isUrl(str) {
  try {
    new URL(str);
    return true;
  } catch {
    return false;
  }
}

/**
 * Remove duplicates from array
 * @param {Array} arr Input array
 * @returns {Array} Array with unique values
 */
function unique(arr) {
  return [...new Set(arr)];
}

/**
 * Capitalize first letter of string
 * @param {string} str Input string
 * @returns {string} Capitalized string
 */
function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Convert string to camelCase
 * @param {string} str Input string
 * @returns {string} camelCase string
 */
function camelCase(str) {
  return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '');
}

/**
 * Shuffle array (Fisher-Yates algorithm)
 * @param {Array} arr Array to shuffle
 * @returns {Array} Shuffled array
 */
function shuffle(arr) {
  const array = [...arr];
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

/**
 * Sleep for specified time
 * @param {number} ms Time in milliseconds
 * @returns {Promise} Promise that resolves after delay
 */
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export {
  deepClone,
  cloneArray,
  debounce,
  isEqual,
  throttle,
  random,
  formatTime,
  isUrl,
  unique,
  capitalize,
  camelCase,
  shuffle,
  sleep
};